Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links

Introducción a las Transformaciones 3D

La posibilidad de incursionar en el terreno de las representaciones tridimensionales produce a priori la impresion de una excesiva dificultad, sin embargo las bases de estas tecnicas, son sencillas, y se pueden obtener resulatados sorprendentes con bastante poco esfuerzo.
Utilizo el compilador BorlandC++ 3.1 y las librerias BGI, las limitaciones son la cantidad de vertices y poligonos que puede tener el modelo, ya que se trata de un compilador de 16 Bits y estoy limitado por los 64K de datos que se puede utilizar ya que no utilizo asignacion dinamica de memoria, por no complicar el código.
La imagen de abajo se realizó con el programa presentado aqui, se trata de un toro compuesto por 512 vertices y 1024 poligonos.

Para la visualización de un objeto 3D, se requieren 3 pasos:

Base de Datos del Objeto 3D

Mi objeto esta compuesto por polígonos de tres lados, y cada poligono tiene 3 vértices cuyas coordenadas son (x,y,z).

Número máximo de vértices que puede tener el objeto
#define NUM_VERTICES 
Cantidad máxima de polígonos que tiene el objeto
#define NUM_FACES

Definición de un vértice
typedef struct VTX
{
 double x, y, z;
};

Definición de un polígono
typedef struct TRI
{
 int a, b, c;
};

Estructura para el ordenamiento qsort, donde para cada polígono
almaceno la cordenada z y el indice del polígono
d=coordenada z del poligono, index=indice del poligono
typedef struct LSTRI
{
 int d;
 int index;
};

Tabla que contiene los vértices originales
VTX points[NUM_VERTICES];
Tabla con los vértices transformados
VTX outpoints[NUM_VERTICES];
Tabla que contiene los polígonos
TRI faces[NUM_FACES];
Tabla de polígonos para aplicar  el ordenamiento qsort
LSTRI ListFaces[NUM_FACES];
Número de vértices, y  polígonos del objeto
int vertices, poligonos;

malla.jpg
Vértices del objeto V0 = points[0].x, points[0].y, points[0].z V1 = points[1].x, points[1].y, points[1].z .. .... .... .... V7 = points[7].x, points[7].y, points[7].z Polígonos del objeto faces[0].a = 0 faces[0].b = 2 faces[0].c = 1 faces[1].a = 2 faces[1].b = 3 faces[1].c = 1 .... .... .... faces[5].a = 6 faces[5].b = 7 faces[5].c = 5

Matrices de Transformación 3D

Sistema de Coordenadas

sistema_xyz.jpg El sistema de coordenadas tridimensional utilizado es el llamado de la mano izquierda

Matriz Identidad

La matriz identidad se representa como matriz_identidad :

  1  0  0  0
  0  1  0  0
  0  0  1  0
  0  0  0  1

Escalado

El escalado consiste en multiplicar las coordenadas del objeto por una constante, para modificar su tamaño. Se describe el escalamiento cuando el punto fijo es el origen. Para obtener un escalamiento con un punto fijo arbitrario, este se debe trasladar al origen, escalar el objeto, y despues realizar el inverso de la traslacion original.
La matriz de escalamiento con factores de escala Ex, Ey, Ez en las direcciones x, y, z respectivamente, esta dada por la matriz Es(Ex, Ey, Ez) :

  Ex  0   0   0
  0   Ey  0   0
  0   0   Ez  0
  0   0   0   1

La inversa de un escalamiento se obtiene usando los reciprocos de los factores de escala : 1/Ex, 1/Ey, 1/Ez.

Rotación en torno al eje X

La rotación de un angulo a sobre el eje x se representa por medio de la matriz Rx(a) :

      1        0       0      0
      0      cos(a)  sin(a)   0
      0     -sin(a)  cos(a)   0
      0        0       0      1
rotacion_x.jpg

Código en C

void matriz_rotacion_x(MATRIZ *m, double angulo)
{
 double  tmpsin, tmpcos;

 tmpsin=sin(angulo);
 tmpcos=cos(angulo);
 *m = matriz_identidad;
 m->v[1][1] =  tmpcos;
 m->v[1][2] =  tmpsin;
 m->v[2][1] = -tmpsin;
 m->v[2][2] =  tmpcos;
}

Ejemplo: matriz_rotacion_x(&Rx, 0.7)
Esto crea la matriz de rotación alrededor del eje X, donde Rx es una matriz 4x4 y 0.7 es el angulo de rotación, en radianes.


Rotación en torno al eje Y

La rotación de un punto alrededor del eje Y se realiza en un plano paralelo al plano x, z. El valor de la coordenada Y no cambia. La matriz de rotacion es Ry(a) :

    cos(a)     0    -sin(a)   0
      0        1       0      0
    sin(a)     0     cos(a)   0
      0        0       0      1
rotacion_y.jpg

Código en C.

void matriz_rotacion_y(MATRIZ *m, double angulo)
{
 double  tmpsin, tmpcos;

 tmpsin=sin(angulo);
 tmpcos=cos(angulo);
 *m = matriz_identidad;
 m->v[0][0] =  tmpcos;
 m->v[0][2] = -tmpsin;
 m->v[2][0] =  tmpsin;
 m->v[2][2] =  tmpcos;
}

Ejemplo : matriz_rotacion_y(&Ry, 0.7)
donde Ry es la matriz de rotación 4x4, y 0.7 es el angulo de rotacióon.


Rotación en torno al eje Z

La rotación de un angulo a sobre el eje Z se representa por medio de la matriz Rz(a) :

    cos(a)   sin(a)    0      0
   -sin(a)   cos(a)    0      0
      0        0       1      0
      0        0       0      1
rotacion_z.jpg

Código en C

void matriz_rotacion_z(MATRIZ *m, double angulo)
{
 double  tmpsin, tmpcos;

 tmpsin=sin(angulo);
 tmpcos=cos(angulo);
 *m = matriz_identidad;
 m->v[0][0] =  tmpcos;
 m->v[0][1] =  tmpsin;
 m->v[1][0] = -tmpsin;
 m->v[1][1] =  tmpcos;
}

Ejemplo : matriz_rotacion_z(&Rz, 0.7)
donde Rz es la matriz de 4x4, y 0.7 es el angulo de rotación en torno al eje Z.


Traslación

La matriz asociada a la traslación es la siguiente Tr(Tx, Ty, Tz) :

  1   0   0   0
  0   1   0   0
  0   0   1   0
  Tx  Ty  Tz  1

Código en C

void matriz_traslacion(MATRIZ *m, double Tx, double Ty, double Tz)
{
*m = matriz_identidad;
 m->v[3][0] = Tx;
 m->v[3][1] = Ty;
 m->v[3][2] = Tz;
}

Ejemplo : matriz_traslacion(&Tr, 0.0, 0.0, -350.0)
donde Tr es la matriz de traslación 4x4, y -100.0 es el desplazamiento en el eje Z.
La matriz de traslación inversa se obtiene invirtiendo el signo de los valores de Tx, Ty, Tz. Es decir Tr(-Tx,-Ty,-Tz)


Importante

Hay que tener en cuenta que no es lo mismo rotar y luego trasladar, que trasladar y luego rotar: Si se rota el objeto y luego se traslada, aparecerá el objeto (rotado sobre si mismo) en el punto correspondiente a la traslación, en cambio, si primero se traslada y luego se rota, se obtiene algo totalmente distinto, pues el objeto no rota sobre si mismo, sino que lo hace respecto al origen de coordenadas de la escena. Esto es debido a que el eje sobre el que se realiza la rotación pasa por el origen de coordenadas, que será el origen relativo del objeto o el global de las escena. Si se traslada el objeto, el origen de coordenadas correspondera a un punto distinto del objeto.

Construcción de la matriz de Transformación

Todo esta listo para construir la matriz de transformación, para ello utilizo las matrices Rx, Ry, Rz y Tr :
void matriz_multiplicacion(MATRIZ *m1, MATRIZ *m2, MATRIZ *out)
que efectua el producto de dos matrices m1, m2, de 4x4 y almacena el resultado en una matriz out de 4x4.

temp = Rx x Ry
matriz_multiplicacion(&Rx, &Ry, &temp)
Rot = temp x Rz
matriz_multiplicacion(&temp, &Rz, &Rot)
Mat = Rot x Tr
matriz_multiplicacion(&Rot, &Tr, &Mat)

donde temp, Rot y Mat son matrices de 4x4, Mat es la matriz de transformación.

Proyección en Perspectiva

Una vez construida la matriz de transformación Mat, tenemos que aplicarla a todos los vertices del objeto, utilizamos para ello :
aplicar_matriz(&Mat, points[i].x, points[i].y, points[i].z, &outpoints[i].x, &outpoints[i].y, &outpoints[i].z)
me multiplica cada uno de los vertices del objeto (points[]) por la matriz de transformación Mat, el resultado es el vertice transformado (outpoints[]).

Proyección en Perspectiva

Esta proyección es una transformación que convierte una representación tridimencional en bidimencional, para poder representarla en la pantalla, D es la distancia entre el plano de proyeccion (pantalla) y el obeservador, con valores pequenos la imagen se distorciona, con D=800, anda todo bien, persp_xoffset, persp_yoffset son las coordenadas del centro de la pantalla, por ejemplo si trabajo en el modo 640x480, persp_xoffset=320 persp_yoffset=240.

void persp_projeccion(double x, double y, double z, double *xout, double *yout)
{
 double z1 = 1.0 / z;
 *xout = ((x * z1) * D) + persp_xoffset;
 *yout = ((y * z1) * D) + persp_yoffset;
}

Ejemplo : persp_projeccion(outpoints[i].x, outpoints[i].y, outpoints[i].z, &outpoints[i].x, &outpoints[i].y)

La salida son puntos que se pueden representar en la pantalla.

Determinación de las caras visibles del objeto

Primero realizó una clasificación por profundidad para cada polígono utilizando qsort, ordenación rapida, y usando las coordenadas z. Luego para cada uno de estos polígonos uso el algoritmo de Backface-Culling para determinar si es visible o no por el observador.

Backface-Culling de poligonos

Para determinar que polígonos son visibles desde el punto de vista del observador debemos realizar los siguientes pasos :

double Hidden(VTX v1, VTX v2, VTX v3)
{
 VTX u, v;
 VTX out;
 double xout, yout, zout;
 double temp;

 u.x = v2.x - v1.x;
 u.y = v2.y - v1.y;
 u.z = v2.z - v1.z;

 v.x = v3.x - v1.x;
 v.y = v3.y - v1.y;
 v.z = v3.z - v1.z;

 producto_vectorial(u, v, &out);
 temp = producto_escalar(out.x, out.y, out.z, 0.0, 0.0, -1.0);
 return temp;
}

Solo funciona bien si el modelo consiste en un unico solido convexo (esfera), pues si hay mas de un solido, o si este no es convexo (toro), un poligono puede tapar parcialmente a otro poligono (ambos visibles), por lo que es necesario utilizar otro metodo complementario.

Clasificacion por profundidad de los poligonos

Consiste en trazar primero los poligonos situados más lejos. Para ello se ordenan los poligonos en función de su profundidad media, utilizo para ello el metodo de ordenacion rapida qsort.
No siempre funciona bien, porque no siempre todos los vertices de un poligono estan mas cerca o mas lejos que todos los vertices de otro poligono. Aunque es inexacto (especialmente en poligono de gran tamaño).

Iluminación

Este efecto se logra pintando los poligonos con diferentes tonos de grises, para ello utilizo el vector normal al poligono, el cual tiene que ser normalizado, es decir dividir el vector por su modulo, para que sea unitario,y el vector de componentes xlight, ylight, zlight que son las coordenadas de la fuente de luz, este vector tambien tiene que ser normalizado, realizo el producto_escalar entre ambos vectores el valor que me da como resultado me indica la cantidad de luz que recibe cada poligono, 0.0 intensidad minima y 1.0 maxima intensidad.
Utilizo una paleta de 256 colores, en este caso son niveles de grises 64 en total, como el nivel de luz varia entre 0.0 y 1.0, lo escalo multiplicandolo por 256.0, que es la cantidad de colores que tiene mi paleta.
La siguiente funcion me calcula el nivel de luz, que recibe un poligono.

int Light(VTX v1, VTX v2, VTX v3)
{
 VTX u, v, out;
 double temp;

 u.x = v2.x - v1.x;
 u.y = v2.y - v1.y;
 u.z = v2.z - v1.z;
 v.x = v3.x - v1.x;
 v.y = v3.y - v1.y;
 v.z = v3.z - v1.z;
 //out es el vector normal al poligono
 producto_vectorial(u, v, &out);
 vector_normalizado(&out.x, &out.y, &out.z);
 vector_normalizado(&xlight, &ylight, &zlight);
 //El resultado de todas estas operaciones dar  un valor comprendido
 //entre 0.0 (m¡nima luz) y 1.0f (m xima luz)
 temp = producto_escalar(out.x, out.y, out.z, xlight, ylight, zlight);
 temp = 256.0*temp;
 if (temp<0.0)
	temp=0.0;
 else
	if (temp>255.0)
	temp=255.0;
 return (int) (temp);
}

Implementación en C

Se define el objeto a visualizar en un archivo de texto (toro.dat), el cual es leido desde el programa, esta compuesto por 512 vertices y 1024 poligonos de tres vertices. La representación es en modo Flat Shading.
Utilizo las librerias BGI de BorlandC++3.1, con el comando fillpoly() dibujo los poligonos.

Estructura del archivo de texto toro.dat que contiene los datos del objeto.

512  (es el número de vértices que tiene el objeto)
-0.000003 75.000000 0.000000  (coordenadas de los vertices)
-0.000003 73.096985 9.567086
-0.000003 67.677666 17.677670
.......... .......... .........
1024  (es el numero de poligonos del objeto)
0 17 16   (indice de los vertices que forman cada poligono)
0 1 17
1 18 17

. . .....

Imagen generada por el programa, utilizando una paleta de 64 tonos de grises.

Código Fuente

Codigo Fuente Artic1.zip
Archivo que contiene los datos del objeto 3d.zip
Archivo que contiene los datos del objeto toro.zip Archivos svga256.bgi y svga256.h, necesarios para la compilacion svga256.zip

/*
 * Ejemplo de transformaciones 3D utilzando matrices 4x4
 *
 * Este programa carga la geometria  del objeto, es decir
 * las coordenadas de los vertices, y las caras desde una
 * archivo de texto (toro.dat), y le aplica las transformaciones
 * necesarias para lograr una proyeccion en perspectiva, para
 * ello utiliza matrices 4x4.
 * La representacion es en modo flat, son eliminadas las caras
 * que no son visbles.
 *
 * El codigo no esta optimizdo, ya que la finalidad de este
 * programa es mostrar el uso de las matrices de transformacion.
 * Fue compilado con BorlandC++3.1, utiliza las librerias BGI
 *
 * www.oocities.org/valcoey/index.html
 * valcoey@hotmail.com
 * Ramiro Alcocer
 * 12/01/2001
 */

#include <graphics.h>
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include "svga256.h"

#define NUM_VERTICES 512
#define NUM_FACES	 1024

typedef struct MATRIZ
{
 double v[4][4];
};

typedef struct VTX
{
 double x, y, z;
};

typedef struct TRI
{
 int a, b, c;
};

typedef struct LSTRI
{
 int d;
 int index;
};

VTX points[NUM_VERTICES];
VTX outpoints[NUM_VERTICES];
TRI faces[NUM_FACES];
LSTRI ListFaces[NUM_FACES];
int vertices, poligonos;
//posicion de la fuente de luz
double xlight=1.0, ylight=-1.0, zlight=1.0;
unsigned char paleta[256][3];

MATRIZ matriz_identidad = {
	{
	{ 1.0, 0.0, 0.0, 0.0},
	{ 0.0, 1.0, 0.0, 0.0},
	{ 0.0, 0.0, 1.0, 0.0},
	{ 0.0, 0.0, 0.0, 1.0}
	}
};

double persp_xoffset;
double persp_yoffset;
double D;

int huge deteccion()
{
 int modo;

 clrscr();
 printf("Selecciona un modo de video...\n");
 printf("  0) 320x200x256  VGA\n");
 printf("  1) 640x400x256  SVGA\n");
 printf("  2) 640x480x256  SVGA\n");
 printf("  3) 800x600x256  SVGA\n");
 printf("  4) 1024x768x256 SVGA\n\n>");
 scanf("%d", &modo);
 return modo;
}

void aplicar_matriz(MATRIZ *m, double x, double y, double z, double *xout, double *yout, double *zout)
{
 *xout = x*m->v[0][0] + y*m->v[1][0] + z*m->v[2][0] + 1.0*m->v[3][0];
 *yout = x*m->v[0][1] + y*m->v[1][1] + z*m->v[2][1] + 1.0*m->v[3][1];
 *zout = x*m->v[0][2] + y*m->v[1][2] + z*m->v[2][2] + 1.0*m->v[3][2];
}

void matriz_multiplicacion(MATRIZ *m1, MATRIZ *m2, MATRIZ *out)
{
 int i, j, k;

 for (i=0; i<4; i++) {
	for (j=0; j<4; j++) {
		out->v[i][j] = 0.0;
		for (k=0; k<4; k++)
			out->v[i][j] = out->v[i][j] + m1->v[i][k]*m2->v[k][j];
		}
	}
}

void matriz_traslacion(MATRIZ *m, double Tx, double Ty, double Tz)
{
*m = matriz_identidad;
 m->v[3][0] = Tx;
 m->v[3][1] = Ty;
 m->v[3][2] = Tz;
}

void matriz_escalamiento(MATRIZ *m, double Ex, double Ey, double Ez)
{
 *m = matriz_identidad;
 m->v[0][0] = Ex;
 m->v[1][1] = Ey;
 m->v[2][2] = Ez;
}

void matriz_rotacion_x(MATRIZ *m, double angulo)
{
 double  tmpsin, tmpcos;

 tmpsin=sin(angulo);
 tmpcos=cos(angulo);
 *m = matriz_identidad;
 m->v[1][1] =  tmpcos;
 m->v[1][2] =  tmpsin;
 m->v[2][1] = -tmpsin;
 m->v[2][2] =  tmpcos;
}

void matriz_rotacion_y(MATRIZ *m, double angulo)
{
 double  tmpsin, tmpcos;

 tmpsin=sin(angulo);
 tmpcos=cos(angulo);
 *m = matriz_identidad;
 m->v[0][0] =  tmpcos;
 m->v[0][2] = -tmpsin;
 m->v[2][0] =  tmpsin;
 m->v[2][2] =  tmpcos;
}

void matriz_rotacion_z(MATRIZ *m, double angulo)
{
 double  tmpsin, tmpcos;

 tmpsin=sin(angulo);
 tmpcos=cos(angulo);
 *m = matriz_identidad;
 m->v[0][0] =  tmpcos;
 m->v[0][1] =  tmpsin;
 m->v[1][0] = -tmpsin;
 m->v[1][1] =  tmpcos;
}

void persp_projeccion(double x, double y, double z, double *xout, double *yout)
{
 double z1 = 1.0 / z;
 *xout = ((x * z1) * D) + persp_xoffset;
 *yout = ((y * z1) * D) + persp_yoffset;
}

void set_projeccion_viewport(int w, int h)
{
 D = 800;
 persp_xoffset = w/2;
 persp_yoffset = h/2;
}

void ver_matriz(MATRIZ *m)
{
 printf("%lf %lf %lf %lf\n", m->v[0][0], m->v[0][1], m->v[0][2], m->v[0][3]);
 printf("%lf %lf %lf %lf\n", m->v[1][0], m->v[1][1], m->v[1][2], m->v[1][3]);
 printf("%lf %lf %lf %lf\n", m->v[2][0], m->v[2][1], m->v[2][2], m->v[2][3]);
 printf("%lf %lf %lf %lf\n", m->v[3][0], m->v[3][1], m->v[3][2], m->v[3][3]);
}

double producto_escalar(double x1, double y1, double z1, double x2, double y2, double z2)
{
 return ((x1 * x2) + (y1 * y2) + (z1 * z2));
}

void producto_vectorial(VTX v1, VTX v2, VTX *out)
{
 out->x = (v1.y * v2.z) - (v1.z * v2.y);
 out->y = (v1.z * v2.x) - (v1.x * v2.z);
 out->z = (v1.x * v2.y) - (v1.y * v2.x);
}

double modulo_vector(double x, double y, double z)
{
 return sqrt(x*x + y*y + z*z);
}

void vector_normalizado(double *x, double *y, double *z)
{
 double length = 1.0 / modulo_vector(*x, *y, *z);

 *x *= length;
 *y *= length;
 *z *= length;
}

//Esta funcion permite conocer si un poligono esta de cara
//o de espaldas al observador, segun devuelva un valor mayor
//o menor que cero
double Hidden(VTX v1, VTX v2, VTX v3)
{
 VTX u, v;
 VTX out;
 double xout, yout, zout;
 double temp;

 u.x = v2.x - v1.x;
 u.y = v2.y - v1.y;
 u.z = v2.z - v1.z;
 v.x = v3.x - v1.x;
 v.y = v3.y - v1.y;
 v.z = v3.z - v1.z;
 producto_vectorial(u, v, &out);
 temp = producto_escalar(out.x, out.y, out.z, 0.0, 0.0, -1.0);
 return temp;
}

//funcion usada para calcular el nivel de intensidad de la
//luz que recibe un poligono
int Light(VTX v1, VTX v2, VTX v3)
{
 VTX u, v, out;
 double temp;

 u.x = v2.x - v1.x;
 u.y = v2.y - v1.y;
 u.z = v2.z - v1.z;
 v.x = v3.x - v1.x;
 v.y = v3.y - v1.y;
 v.z = v3.z - v1.z;
 //out es el vector normal al poligono
 producto_vectorial(u, v, &out);
 vector_normalizado(&out.x, &out.y, &out.z);
 vector_normalizado(&xlight, &ylight, &zlight);
 //El resultado de todas estas operaciones dar  un valor comprendido
 //entre 0.0 (m¡nima luz) y 1.0f (m xima luz)
 temp = producto_escalar(out.x, out.y, out.z, xlight, ylight, zlight);
 temp = 256.0*temp;
 if (temp<0.0)
	temp=0.0;
 else
	if (temp>255.0)
	temp=255.0;
 return (int) (temp);
}

//me dibuja un poligono solido, el color lo determino
//de acuerdo al nivel de luz que recibe.
 void DibujarPoligono(int num)
{
 int A, B, C;
 int poly[6];
 int i, color;

 i=ListFaces[num].index;
 A=faces[i].a;
 B=faces[i].b;
 C=faces[i].c;
 //verifico si el poligono es visible o no
 if (Hidden(outpoints[A], outpoints[B], outpoints[C])<0.0)
	{
	//se llama a la funci¢n que calcula la luz de un pol¡gono
	color = Light(outpoints[A], outpoints[B], outpoints[C]);
	poly[0]=(int) outpoints[A].x;
	poly[1]=(int) outpoints[A].y;
	poly[2]=(int) outpoints[B].x;
	poly[3]=(int) outpoints[B].y;
	poly[4]=(int) outpoints[C].x;
	poly[5]=(int) outpoints[C].y;
	setfillstyle(SOLID_FILL, color);
	setcolor(color);
	fillpoly(3, poly);
	}
}

int comparar(const void * e1, const void * e2)
{
 int d1, d2;

 LSTRI *q1 = (LSTRI *)e1;
 LSTRI *q2 = (LSTRI *)e2;

 d1=q1->d;
 d2=q2->d;
 return (d2-d1);
}

void setvgapalette256(DacPalette256 *PalBuf)
{
  struct REGPACK reg;

  reg.r_ax = 0x1012;
  reg.r_bx = 0;
  reg.r_cx = 256;
  reg.r_es = FP_SEG(PalBuf);
  reg.r_dx = FP_OFF(PalBuf);
  intr(0x10,®);
}

void LeerObjeto(char *nombre)
{
 int i;

 FILE *in = fopen(nombre, "r");
 if (!in)
	return;
 fscanf(in, "%d\n", &vertices);
 //printf("%d\n", vertices);
 for (i = 0; i<vertices; i++)
	{
	fscanf(in, "%lf %lf %lf\n", &points[i].x, &points[i].y, &points[i].z);
	//printf("%lf %lf %lf\n", points[i].x, points[i].y, points[i].z);
	};
 fscanf(in, "%d\n", &poligonos);
 //printf("%d\n", poligonos);
 for (i = 0; i<poligonos; i++)
	{
	fscanf(in, "%d %d %d\n", &faces[i].a, &faces[i].b, &faces[i].c);
	//printf("%d %d %d\n", faces[i].a, faces[i].b, faces[i].c);
	}
 //printf("Presionar una tecla para continuar...");
 //getch();
 fclose(in);
}

int main(void)
{
 MATRIZ Rx, Ry, Rz, Rot;
 MATRIZ Tr, Mat;
 MATRIZ temp;
 int i;


 //Carga el modelo en memoria
 LeerObjeto("toro.dat");

 //Inicializa el modo grafico
 int driver = DETECT, modo = DETECT;
 installuserdriver("Svga256", deteccion);
 initgraph(&driver, &modo, "");

 for (i=0; i<256; i++)
	{
	paleta[i][0]=i/4;
	paleta[i][1]=i/4;
	paleta[i][2]=i/4;
	}

 //me cambia la paleta de colores a una escala de 64 tonos de grises
 setvgapalette256(&paleta);

 for (i=0; i<256; i++)
	{
	setcolor(i);
	line(i,0,i,20);
	};

 set_projeccion_viewport(getmaxx(), getmaxy());

 //Inicializa las matrices de rotacion y
 //traslacion.
 matriz_rotacion_x(&Rx, -0.785);
 matriz_rotacion_y(&Ry, 0.523);
 matriz_rotacion_z(&Rz, 0.0);
 matriz_traslacion(&Tr, 0.0, 0.0, -350.0);
 //multiplica las matrices
 //Mat=Rx*Ry*Rz*Tr  (matriz de transformacion)
 matriz_multiplicacion(&Rx, &Ry, &temp);
 matriz_multiplicacion(&temp, &Rz, &Rot);
 matriz_multiplicacion(&Rot, &Tr, &Mat);
 //aplica la matriz de transformacion a cada uno
 //de los vertices del modelo.
 for (i=0; i<vertices; i++)
	{
	aplicar_matriz(&Mat, points[i].x, points[i].y, points[i].z,
				   &outpoints[i].x, &outpoints[i].y, &outpoints[i].z);
	persp_projeccion(outpoints[i].x, outpoints[i].y, outpoints[i].z,
				  &outpoints[i].x, &outpoints[i].y);
	};
 //construyo una tabla con las coordenadas z, de cada uno
 //de los poligonos.
 for (i=0; i<poligonos; i++)
	{
	ListFaces[i].d = (int) fabs(outpoints[faces[i].a].z + outpoints[faces[i].b].z + outpoints[faces[i].c].z);
	ListFaces[i].index=i;
	}
 //ordeno los poligonos de acuerdo a z.
 qsort(ListFaces, poligonos, sizeof(ListFaces[0]),  comparar);

 //dibujo los poligonos
 for (i=0; i<poligonos; i++)
	DibujarPoligono(i);

 getch();
 closegraph();
 return 0;
}


valcoey@hotmail.com

Ramiro Alcocer, 2001

Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links